package interp;

import ic.ast.Parameter;
import ic.ast.PrimitiveType;
import ic.ast.PrimitiveType.DataType;

import java.util.HashMap;
import java.util.List;
import java.util.Stack;

public class SymbolsMappingHelper {
	final private Stack<HashMap<String, TypedValue>> symbols;

	public SymbolsMappingHelper(Stack<HashMap<String, TypedValue>> symbols) {
		this.symbols = symbols;
	}

	public static Object convertStringByType(String str, DataType dt)
			throws Exception {
		switch (dt) {
		case INT:
			return Integer.parseInt(str);
		case STRING:
			return str;
		case BOOLEAN:
			if (str.equals("true"))
				return true;
			if (str.equals("false"))
				return false;
		default:
			throw new Exception("unable to convert literal type");
		}
	}

	public void assignParameterByString(Parameter p, String arg)
			throws Exception {
		if (p.getType() instanceof PrimitiveType) {
			PrimitiveType pt = (PrimitiveType) p.getType();

			Object val;
			val = convertStringByType(arg, pt.getType());

			symbols.peek().put(p.getName(),
					new TypedValue(pt.getType(), val, pt.getArrayDimension()));

			return;
		}
		throw new Exception("parameter type not in exercise");
	}

	public void assignParameterByString(List<Parameter> params,
			List<String> args) throws Exception {
		for (int i = 0; i < params.size(); i++) {
			assignParameterByString(params.get(i), args.get(i));
		}
	}

	public HashMap<String, TypedValue> lookupVariable(String name) {
		Stack<HashMap<String, TypedValue>> checked = new Stack<HashMap<String, TypedValue>>();
		HashMap<String, TypedValue> found = null;
		while (!symbols.isEmpty()) {
			if (symbols.peek().containsKey(name)) {
				found = symbols.peek();
				break;
			} else {
				checked.push(symbols.pop());
			}
		}
		while (!checked.isEmpty())
			symbols.push(checked.pop());
		return found;
	}

	public Object checkAndAssign(TypedValue variable, TypedValue newval)
			throws Exception {
		if (!newval.initialized)
			throw new Exception("unable to assign non-initialized variable");

		// TypedValue variable = variable_vv.mapped_variable;
		if (newval.type != variable.type)
			throw new Exception("unable to assign between different types");

		if (variable.dimension != newval.dimension)
			throw new Exception("unable to assign between different dimensions");

		if (variable instanceof ArrayCellValue) {
			Object[] arr = (Object[]) ((ArrayCellValue) variable).backing_array
					.getValue();
			return arr[((ArrayCellValue) variable).index] = newval.getValue();
		} else {
			variable.setValue(newval.getValue());
			return variable.getValue();
		}
	}
}
